#include "comment.header"

#import "Board.h"
#import "gnugo.h"
#include "igs.h"

#import <libc.h>
#import <math.h>
#import <dpsclient/wraps.h>	// PSxxx functions
#import <soundkit/Sound.h>
#import <appkit/appkit.h>

#define EMPTY		0
#define WHITESTONE	1
#define BLACKSTONE	2
#define NEUTRAL_TERR	3
#define WHITE_TERR	4
#define BLACK_TERR	5
#define SPECIAL_CHAR    6
#define KOMI            5.5

// The following values are the default sizes for the various pieces. 
  
#define RADIUS		14.5 			// Stone radius
#define STONEWIDTH	29.0			// Stone width
#define STONEHEIGHT	29.0			// Stone height
  
  // SHADOWOFFSET defines the amount the shadow is offset from the piece. 
  
#define SHADOWOFFSET 2.0
  
#define BASEBOARDX 19.0
#define BASEBOARDY 19.0
#define WINDOWOFFSETX 12.0
#define WINDOWOFFSETY 100.0
  
#define gameSize  bounds.size

#define PSLine(a, b, x, y)	PSmoveto(a, b); PSlineto(x, y)

float stoneX, stoneY;
int blackStones, whiteStones;
char currentCharacter;

void setStoneLoc(int x, int y)
{
  stoneX = ((19.0 - MAXX)/2.0)*STONEWIDTH + BASEBOARDX - RADIUS + (x*STONEWIDTH);
  stoneY = BASEBOARDY - RADIUS + ((18 - y)*STONEHEIGHT) - ((19.0 - MAXY)/2.0)*STONEHEIGHT;
}  

@implementation GoView
  
  - initFrame:(const NXRect *)frm
{
  NXSize stoneSize;
  
  stoneSize.width = STONEWIDTH;
  stoneSize.height = STONEHEIGHT;
  
  [super initFrame:frm];
  
  [self allocateGState];	// For faster lock/unlockFocus
    
  [(blackStone = [[NXImage allocFromZone:[self zone]] init]) setScalable:NO];
  [blackStone useDrawMethod:@selector(drawBlackStone:) inObject:self];
  [blackStone setSize:&stoneSize];
  
  [(whiteStone = [[NXImage allocFromZone:[self zone]] init]) setScalable:NO];
  [whiteStone useDrawMethod:@selector(drawWhiteStone:) inObject:self];
  [whiteStone setSize:&stoneSize];
  
  [(grayStone = [[NXImage allocFromZone:[self zone]] init]) setScalable:NO];
  [grayStone useDrawMethod:@selector(drawGrayStone:) inObject:self];
  [grayStone setSize:&stoneSize];
  
  [(upperLeft = [[NXImage allocFromZone:[self zone]] init]) setScalable:NO];
  [upperLeft useDrawMethod:@selector(drawUpperLeft:) inObject:self];
  [upperLeft setSize:&stoneSize];
  
  [(upperRight = [[NXImage allocFromZone:[self zone]] init]) setScalable:NO];
  [upperRight useDrawMethod:@selector(drawUpperRight:) inObject:self];
  [upperRight setSize:&stoneSize];
  
  [(lowerLeft = [[NXImage allocFromZone:[self zone]] init]) setScalable:NO];
  [lowerLeft useDrawMethod:@selector(drawLowerLeft:) inObject:self];
  [lowerLeft setSize:&stoneSize];
  
  [(lowerRight = [[NXImage allocFromZone:[self zone]] init]) setScalable:NO];
  [lowerRight useDrawMethod:@selector(drawLowerRight:) inObject:self];
  [lowerRight setSize:&stoneSize];
  
  [(midLeft = [[NXImage allocFromZone:[self zone]] init]) setScalable:NO];
  [midLeft useDrawMethod:@selector(drawMidLeft:) inObject:self];
  [midLeft setSize:&stoneSize];
  
  [(midRight = [[NXImage allocFromZone:[self zone]] init]) setScalable:NO];
  [midRight useDrawMethod:@selector(drawMidRight:) inObject:self];
  [midRight setSize:&stoneSize];
  
  [(midTop = [[NXImage allocFromZone:[self zone]] init]) setScalable:NO];
  [midTop useDrawMethod:@selector(drawMidTop:) inObject:self];
  [midTop setSize:&stoneSize];
  
  [(midBottom = [[NXImage allocFromZone:[self zone]] init]) setScalable:NO];
  [midBottom useDrawMethod:@selector(drawMidBottom:) inObject:self];
  [midBottom setSize:&stoneSize];
  
  [(innerSquare = [[NXImage allocFromZone:[self zone]] init]) setScalable:NO];
  [innerSquare useDrawMethod:@selector(drawInnerSquare:) inObject:self];
  [innerSquare setSize:&stoneSize];
  
  [(innerHandicap = [[NXImage allocFromZone:[self zone]] init]) setScalable:NO];
  [innerHandicap useDrawMethod:@selector(drawInnerHandicap:) inObject:self];
  [innerHandicap setSize:&stoneSize];
  
  [self setBackgroundFile:NXGetDefaultValue([NXApp appName], "BackGround") 
 andRemember:NO];
  
  [self startNewGame];

  historyFont = [Font newFont:"Helvetica" size:9.0 matrix:NX_IDENTITYMATRIX];
  blackTerrFont = [Font newFont:"Helvetica" size:25.0 matrix:NX_IDENTITYMATRIX];
  whiteTerrFont = [Font newFont:"Helvetica" size:22.5 matrix:NX_IDENTITYMATRIX];
  stoneClick = [Sound findSoundFor:"Pop"];
  
  return self;
}

// free simply gets rid of everything we created for MainGoView, including
  // the instance of MainGoView itself. This is how nice objects clean up.
  
  - free
{
  [backGround free];
  return [super free];
}



// This methods allows changing the file used to paint the background of the
  // playing field. Set fileName to NULL to revert to the default. Set
  // remember to YES if you wish the write the value out in the defaults.
  
  - setBackgroundFile:(const char *)fileName andRemember:(BOOL)remember
{
  [backGround free];
  backGround = [[NXImage allocFromZone:[self zone]] initSize:&gameSize];
  if (fileName) {
    [backGround useFromFile:fileName];
    if (remember) {
      NXWriteDefault ([NXApp appName], "Background", fileName);
    }
  } else {
    [backGround useFromSection:"Background.tiff"];
    if (remember) {
      NXRemoveDefault ([NXApp appName], "Background");
    }
  }
  [backGround setBackgroundColor:NX_COLORWHITE];
  [backGround setScalable:NO];
  [self display];
  
  return self;   
}

// The following two methods allow changing the background image from
  // menu items or buttons.
  
  - changeBackground:sender
{
  const char *const types[] = {"tiff", "eps", NULL};  
  
  if ([[OpenPanel new] runModalForTypes:types]) {
    [self setBackgroundFile:[[OpenPanel new] filename] andRemember:YES];
    [self display];
  }
  
  return self;
}

- revertBackground:sender
{
  [self setBackgroundFile:NULL andRemember:YES];
  [self display];
  return self;
}

// The following method will initialize all the variables for a new game.
  
  - startNewGame
{
  int i, j;
  
  gameRunning = NO;
  finished = NO;
  gameScored = NO;
  resultsDisplayed = NO;
  scoringGame = NO;

  lastMove = 0;
  
  blackCaptured = whiteCaptured = 0;
  manualScoring = manScoreTemp;
  
  seed(&rd);
  
  for (i = 0; i < MAXX; i++)
    for (j = 0; j < MAXY; j++)
      p[i][j] = hist[i][j] = 0;
  
  for (i = 0; i < 9; i++)
    opn[i] = 1;
  opn[4] = 0;

  if (gameType == LOCAL)
    {
      sethand(handicap);
      currentStone = (handicap == 0)?BLACKSTONE:WHITESTONE;
      opposingStone = (currentStone == BLACKSTONE)?WHITESTONE:BLACKSTONE;
  
      if (currentStone == BLACKSTONE)
	[gameMessage setStringValue:"Black's Turn"];
      else
	[gameMessage setStringValue:"White's Turn"];

      if (SmartGoGameFlag)
	{
	  [startButton setEnabled: NO];
	  [stopButton setEnabled: NO];
	  [passButton setEnabled: NO];
	  [startButton display];
	  [stopButton display];
	  [passButton display];
	}
    
      if (bothSides)
	[passButton setEnabled: NO];
      else
	[passButton setEnabled: YES];
      [passButton display];
  
      if (neitherSide) {
	[startButton setEnabled: NO];
	[stopButton setEnabled: NO];
	[startButton display];
	[stopButton display];
      } else {
	[startButton setEnabled: YES];
	[stopButton setEnabled: YES];
	[startButton display];
	[stopButton display];
      }
  
      if (((currentStone == BLACKSTONE) && (blackSide == 1)) ||
	  ((currentStone == WHITESTONE) && (whiteSide == 1)))  
	[gameMessage2 setStringValue:"Press START to begin..."];
      else
	[gameMessage2 setStringValue:"You move first..."];
      [self setblacksPrisoners:0];
      [self setwhitesPrisoners:0];
    }
  else
    {
      [gameMessage2 setStringValue:"Internet Go Server"];
      [gameMessage setStringValue:""];
      [self setblacksPrisoners:0];
      [self setwhitesPrisoners:0];
      [passButton setEnabled: YES];
      [startButton setEnabled: NO];
      [stopButton setEnabled: NO];
      [passButton display];
      [startButton display];
      [stopButton display];
    }
    
  [ScoringWindow close];
  NXPing();
    
  return self;
}

// The stop method will pause a running game. The go method will start it up
  // again.
  
- go:sender
{
  if (gameType == IGSGAME)
    return self;
    
  if ((scoringGame) && (manualScoring))
    {
      int i, j;

      find_owner();
      blackTerritory = 0;
      whiteTerritory = 0;

      for (i = 0; i < MAXX; i++)
	for (j = 0; j < MAXY; j++)
	  {
	    if (ownermat[i][j] == BLACKSTONE)
	      {
		blackTerritory++;
		p[i][j] = BLACK_TERR;
	      }
	    if (ownermat[i][j] == WHITESTONE)
	      {
		whiteTerritory++;
		p[i][j] = WHITE_TERR;
	      }
	    if (ownermat[i][j] == NEUTRAL_TERR)
	      {
		p[i][j] = NEUTRAL_TERR;
	      }
	  }

      gameScored = YES;
      [self displayScoringInfo];
      NXPing();
    }

  if ((gameRunning == 0) && (finished == 0)) {
    gameRunning = YES;
    [self step];
  }
  return 0;
}

- stop:sender
{
  if (gameType == IGSGAME)
    return self;
      
  if (gameRunning) {
    gameRunning = NO;
  }
  return self;
}

- showLastMove:sender
{
  if (SmartGoGameFlag)
    return self;

  if (finished)
    {
      NXRunAlertPanel("NeXTGo", "The game has concluded.  The last move was\n\
the scoring.", "OK", 0, 0);

      return self;
    }

  if (lastMove == 0)
    {
      NXRunAlertPanel("NeXTGo", "The game has not yet started.", "OK", 0, 0);

      return self;
    }

  if (gameMoves[lastMove-1].x < 0)
    {
      NXRunAlertPanel("NeXTGo", "The last move was a pass.", "OK", 0, 0);

      return self;
    }
    
  setStoneLoc(gameMoves[lastMove-1].x, gameMoves[lastMove-1].y);
  [self lockFocus];
  [self showGrayStone];
  [self unlockFocus];
  NXPing();
  
  [self lockFocus];
  [[self window] flushWindow];
  [self drawSelf:&bounds :0];
  [self display];
  [self unlockFocus];
  NXPing();
  
  return self;
}

- undo
{
  int i, j;

  if (finished)
    return self;
  
  if (lastMove == 1)
    {
      [self startNewGame];
      [self display];

      return self;
    }
    
  if (lastMove > 0)
    {
      for (i = 0; i < MAXX; i++)
	for (j = 0; j < MAXY; j++)
	  p[i][j] = 0;
      blackCaptured = 0;
      whiteCaptured = 0;

      lastMove--;

      for (i = 0; i < lastMove; i++)
	{
	  if (gameMoves[i].x >= 0)
	    {
	      blackPassed = whitePassed = 0;
	      p[gameMoves[i].x][gameMoves[i].y] = gameMoves[i].color;

	      currentStone = gameMoves[i].color;
	      opposingStone = (currentStone == BLACKSTONE)?WHITESTONE
		:BLACKSTONE;
	      examboard(opposingStone);
	    }
	  else
	    {
	      blackPassed = (gameMoves[i].color == BLACKSTONE)?1:0;
	      whitePassed = (gameMoves[i].color == WHITESTONE)?1:0;
	    }
	}
      [self refreshIO];

      currentStone = opposingStone;
      opposingStone = (currentStone == BLACKSTONE)?WHITESTONE:BLACKSTONE;

      if (gameType == LOCAL)
	{
	  [gameMessage setStringValue:(currentStone == BLACKSTONE)?
	   "Black's Turn":"White's Turn"];
	  
	  if ((bothSides) || (((currentStone == BLACKSTONE) && (blackSide)) ||
			      ((currentStone == WHITESTONE) && (whiteSide))))
	    {
	      [gameMessage2 setStringValue:"Press START to continue..."];
	      gameRunning = 0;
	    }
	  else
	    [gameMessage2 setStringValue:"Your move..."];
	}
    }

  return self;
}

- undoLastMove:sender
{
  if (SmartGoGameFlag)
    return self;

  if (gameType == LOCAL)
    {
      [self undo];
    }
  else
    {
      sendstr("undo\n");
    }

  return self;
}

- toggleShowHistFlag:sender
{
  if (SmartGoGameFlag)
    return self;

  [self lockFocus];
  [self display];
  [self unlockFocus];

  return self;
}

- toggleSound:sender
{

  return self;
}

- toggleCoords:sender
{
  [self lockFocus];
  [self display];
  [self unlockFocus];

  return self;
}

- mouseDown:(NXEvent *)event
{
  NXPoint pickedP;

  if (gameType == LOCAL)
    {
      if ((((currentStone == BLACKSTONE) && (blackSide == 1)) ||
	   ((currentStone == WHITESTONE) && (whiteSide == 1))) &&
	  (!scoringGame) && (!manualScoring))
	return self;

      if (SmartGoGameFlag)
	return self;
    
      if ((!gameRunning) && (!finished))
	gameRunning = YES;
  
      if (!finished) {
	int x, y;

	pickedP = event->location;
    
	x = floor((pickedP.x - ((19.0 - MAXX)/2.0)*STONEWIDTH - BASEBOARDX -
		   WINDOWOFFSETX + RADIUS)/STONEWIDTH);
	y = 18 - floor((pickedP.y - BASEBOARDY - WINDOWOFFSETY + RADIUS +
			((19.0 - MAXY)/2.0)*STONEHEIGHT)/STONEHEIGHT);
    
	if (x < 0) x = 0;
	if (x > MAXX - 1) x = MAXX - 1;
	if (y < 0) y = 0;
	if (y > MAXY - 1) y = MAXY - 1;
    
	if ((p[x][y] == 0) && (!suicide(x,y))) {
	  p[x][y] = currentStone;
	  if (currentStone == BLACKSTONE)
	    blackPassed = 0;
	  else
	    whitePassed = 0;

	  [self addMoveToGameMoves: currentStone: x: y];
      
	  setStoneLoc(x,y);
      
	  [self lockFocus];
	  switch (p[x][y])
	    {
	    case WHITESTONE: [self showWhiteStone];
	      break;
	    case BLACKSTONE: [self showBlackStone];
	      break;
	    default: break;
	    }
	  [self unlockFocus];

	  if ([playSounds intValue])
	    [stoneClick play];
	  NXPing();

	  [self updateInfo];

	  if (!neitherSide)
	    [self step];
	} 
      } else {
	if ((scoringGame) && (manualScoring) && (!gameScored))
	  {
	    int x, y;

	    pickedP = event->location;
    
	    x = floor((pickedP.x - ((19.0 - MAXX)/2.0)*STONEWIDTH - BASEBOARDX
		       - WINDOWOFFSETX + RADIUS)/STONEWIDTH);
	    y = 18 - floor((pickedP.y - BASEBOARDY - WINDOWOFFSETY + RADIUS +
			    ((19.0 - MAXY)/2.0)*STONEHEIGHT)/STONEHEIGHT);
    
	    if (x < 0) x = 0;
	    if (x > MAXX - 1) x = MAXX - 1;
	    if (y < 0) y = 0;
	    if (y > MAXY - 1) y = MAXY - 1;

	    if (p[x][y] != EMPTY)
	      {
		int k, l;

		currentStone = p[x][y];

		find_pattern_in_board(x, y);
		for (k = 0; k < MAXX; k++)
		  for (l = 0; l < MAXY; l++)
		    if (patternmat[k][l])
		      {
			p[k][l] = EMPTY;
			if (currentStone == BLACKSTONE)
			  blackCaptured++;
			else
			  whiteCaptured++;
		      }
		
		[self setblacksPrisoners:blackCaptured];
		[self setwhitesPrisoners:whiteCaptured];

                [self lockFocus];
		[self display];
		[self unlockFocus];
	      }
	  }
      }
    }
  else
    {
      int x, y;
      char s[5], n[5];
      extern int observing, ingame;

      if (observing || (ingame == -1))
	{
	  NXRunAlertPanel("IGS Error", "You cannot make a move unless you are playing.",
			  "OK", 0, 0);

	  return self;
	}

      pickedP = event->location;
    
      x = floor((pickedP.x - ((19.0 - MAXX)/2.0)*STONEWIDTH - BASEBOARDX -
		 WINDOWOFFSETX + RADIUS)/STONEWIDTH);
      y = 18 - floor((pickedP.y - BASEBOARDY - WINDOWOFFSETY + RADIUS +
		      ((19.0 - MAXY)/2.0)*STONEHEIGHT)/STONEHEIGHT);

      if (x < 0) x = 0;
      if (x > MAXX - 1) x = MAXX - 1;
      if (y < 0) y = 0;
      if (y > MAXY - 1) y = MAXY - 1;

      s[0] = x + 'a';
      if (x > 7)
        s[0] = x + 'b';
      s[1] = 0;
      sprintf(n, "%d", MAXY-y);
      strcat(s, n);
      strcat(s, "\n");

      sendstr(s);
    }

  return self;
}

- passMove
{
  if (gameType == LOCAL)
    {
      if (((currentStone == BLACKSTONE) && (blackSide == 1)) ||
	  ((currentStone == WHITESTONE) && (whiteSide == 1)))
	return self;

      if (currentStone == BLACKSTONE)
	{
	  blackPassed = 1;
	  if (AGAScoring) blackCaptured++;
	}
      else
	{
	  whitePassed = 1;
	  if (AGAScoring) whiteCaptured++;
	}

      [self addMoveToGameMoves: currentStone: -1: -1];
    
      [self updateInfo];
      if ((!neitherSide) && (!finished))
	[self step];
    }
  else
    {
      sendstr("pass\n");
    }
    
  return self;
}

- refreshIO
{
  [self setblacksPrisoners:blackCaptured];
  [self setwhitesPrisoners:whiteCaptured];
  
  [self lockFocus];
  [[self window] flushWindow];
  [self drawSelf:&bounds :0];
  [self display];
  [self unlockFocus];
  
  NXPing();

  return self;
}

- addMoveToGameMoves: (int)color: (int)x: (int)y
{
  gameMoves[lastMove].color = color;
  gameMoves[lastMove].x = x;
  gameMoves[lastMove].y = y;
  lastMove++;

  if (x >= 0)
    hist[x][y] = lastMove;

  return self;
}

- makeMove: (int)color: (int)x: (int)y
{
  int oldwhitesPrisoners, oldblacksPrisoners, i, j;
  unsigned char old_Board[19][19];

  [self addMoveToGameMoves: color: x: y];

  if ((x >= 0) && (y >= 0))
    {
      p[x][y] = color;
      
      setStoneLoc(x,y);

      [self lockFocus];
      switch (p[x][y])
        {
        case WHITESTONE: [self showWhiteStone];
          break;
        case BLACKSTONE: [self showBlackStone];
          break;
        default: break;
        }
      [self unlockFocus];

      if ([playSounds intValue])
        [stoneClick play];
      NXPing();

      currentStone = color;
      opposingStone = (currentStone == BLACKSTONE)?WHITESTONE:BLACKSTONE;

      oldblacksPrisoners = blackCaptured;
      oldwhitesPrisoners = whiteCaptured;
      for (i = 0; i < MAXX; i++)
        for (j = 0; j < MAXX; j++)
          old_Board[i][j] = p[i][j];
      
      examboard(opposingStone);
  
      [gameMessage setStringValue:(opposingStone == BLACKSTONE)?"Black's Turn":
       "White's Turn"];
      [self setblacksPrisoners:blackCaptured];
      [self setwhitesPrisoners:whiteCaptured];
  
      if (((oldblacksPrisoners != blackCaptured) ||
           (oldwhitesPrisoners != whiteCaptured)))
        {
          [self lockFocus];
	  [self display];
          [self unlockFocus];
        }

      if ([showHistFlag intValue])
        {
          NXRect tmpRect = {{floor(stoneX), floor(stoneY)},
	    		      {floor(STONEWIDTH), floor(STONEHEIGHT)}};

          [self lockFocus];
          [self drawSelf:&tmpRect :0];
          [self display];
          [self unlockFocus];
        }
    }

  return self;
}

- makeMoveSilent: (int)color: (int)x: (int)y
{
  [self addMoveToGameMoves: color: x: y];
      
  if ((x >= 0) && (y >= 0))
    {
      p[x][y] = color;

      currentStone = color;
      opposingStone = (currentStone == BLACKSTONE)?WHITESTONE:BLACKSTONE;

      examboard(opposingStone);
    }
  
  return self;
}

- dispTime: (int)btime: (int)bbyo: (int)wtime: (int)wbyo
{
  char bltime[25], whtime[25];

  if (btime != -1)
    {
      if (btime < 0)
	btime = 0;
      if (wtime < 0)
	wtime = 0;
      sprintf(bltime, "%d:%02d", btime / 60, btime % 60);
      if (bbyo != -1)
	sprintf(bltime, "%s, %d", bltime, bbyo);
      sprintf(whtime, "%d:%02d", wtime / 60, wtime % 60);
      if (wbyo != -1)
	sprintf(whtime, "%s, %d", whtime, wbyo);
      [blackTime setStringValue:bltime];
      [blackTime display];
      [whiteTime setStringValue:whtime];
      [whiteTime display];
    }

  return self;
}

- setGameNumber: (int)n
{
  [IGSGameNumber setIntValue:n];
  [IGSGameNumber display];
  
  return self;
}

- setWhiteName: (char *)wname
{
  [IGSWhitePlayer setStringValue:wname];
  [IGSWhitePlayer display];
  
  return self;
}

- setBlackName: (char *)bname
{
  [IGSBlackPlayer setStringValue:bname];
  [IGSBlackPlayer display];
  
  return self;
}

- setIGSHandicap: (int)h
{
  [IGShandicap setIntValue:h];
  [IGShandicap display];

  return self;
}

- setIGSKomi: (char *)k
{
  [IGSkomi setStringValue:k];
  [IGSkomi display];

  return self;
}

- updateInfo
{
  int oldblacksPrisoners, oldwhitesPrisoners, i, j;
  unsigned char old_Board[19][19];
  
  if (finished && gameScored && resultsDisplayed)
    {
      [startButton setEnabled: NO];
      [stopButton setEnabled: NO];
      [passButton setEnabled: NO];
      [startButton display];
      [stopButton display];
      [passButton display];
      return self;
    }
  
  oldblacksPrisoners = blackCaptured;
  oldwhitesPrisoners = whiteCaptured;
  for (i = 0; i < MAXX; i++)
    for (j = 0; j < MAXX; j++)
      old_Board[i][j] = p[i][j];
      
  examboard(opposingStone);
  
  if (currentStone == BLACKSTONE) {
    opposingStone = BLACKSTONE;
    currentStone = WHITESTONE;
    [gameMessage setStringValue:"White's Turn"];
  } else {
    opposingStone = WHITESTONE;
    currentStone = BLACKSTONE;
    [gameMessage setStringValue:"Black's Turn"];
  }
  
  [self setblacksPrisoners:blackCaptured];
  [self setwhitesPrisoners:whiteCaptured];
  
  if (((oldblacksPrisoners != blackCaptured) ||
      (oldwhitesPrisoners != whiteCaptured)))
    {
      [self lockFocus];
      for (i = 0; i < MAXX; i++)
	for (j = 0; j < MAXX; j++)
	  if ((old_Board[i][j] != EMPTY) && (p[i][j] == EMPTY))
	    {
	      setStoneLoc(i, j);
	      [self eraseStone];
	      [self showBackgroundPiece: i: j];
	    }
    [self unlockFocus];
  }

  if ([showHistFlag intValue])
    {
      NXRect tmpRect = {{floor(stoneX), floor(stoneY)},
			  {floor(STONEWIDTH), floor(STONEHEIGHT)}};

      [self lockFocus];
      [self drawSelf:&tmpRect :0];
      [self display];
      [self unlockFocus];
    }
  
  if ((blackPassed) && (opposingStone == BLACKSTONE))
    [gameMessage2 setStringValue:"Black has Passed."];
    
  if ((whitePassed) && (opposingStone == WHITESTONE))
    [gameMessage2 setStringValue:"White has Passed."];
    
  if ((!blackPassed) && (!whitePassed))
    [gameMessage2 setStringValue:""];
  
  if ((blackPassed) && (whitePassed) && (!manualScoring) && (!gameScored))
    {
      [self lockFocus];
      [[self window] flushWindow];
      [gameMessage setStringValue:"Scoring Game, Please Wait"];
      [gameMessage2 setStringValue:"Removing Dead Groups..."];
      [self display];
      [self unlockFocus];
      finished = 1;
      score_game();
//      [self scoreGame];
      manualScoring = 1;
    }
  if ((blackPassed) && (whitePassed) && (manualScoring) && (!gameScored))
    {
      [self lockFocus];
      [[self window] flushWindow];
      [gameMessage setStringValue:"Please remove dead groups"];
      [gameMessage2 setStringValue:"When finished, press Start..."];
      [self display];
      [self unlockFocus];
      [passButton setEnabled:NO];
      [passButton display];
      [stopButton setEnabled:NO];
      [stopButton display];
      finished = 1;
      scoringGame = YES;
    }

  return self;

}

- displayScoringInfo
{
  char s[35];
  int i, j;

  if (gameScored)
    {
      resultsDisplayed = YES;
      if (typeOfScoring == 0)
	{
	  black_Score = (float)blackTerritory - (float)blackCaptured;
	  white_Score = (float)whiteTerritory - (float)whiteCaptured;
	  white_Score += (handicap == 0)?KOMI:0.5;
	  [TypeOfScoring setStringValue:"Japanese Scoring Method"];
	  [BlackTerrString setStringValue:"Territory"];
	  [WhiteTerrString setStringValue:"Territory"];
	  [BlackTerrValue setIntValue:blackTerritory];
	  [WhiteTerrValue setIntValue:whiteTerritory];
	  [BlackPrisonString setStringValue:"Prisoners"];
	  [WhitePrisonString setStringValue:"Prisoners"];
	  [BlackPrisonValue setIntValue:blackCaptured];
	  [WhitePrisonValue setIntValue:whiteCaptured];
	  [BlackTotalValue setFloatValue:black_Score];
	  [WhiteTotalValue setFloatValue:white_Score];
	}
      else
	{
	  blackStones = whiteStones = 0;
	  for (i = 0; i < MAXX; i++)
	    for (j = 0; j < MAXY; j++)
	      {
		if (p[i][j] == BLACKSTONE) blackStones++;
		if (p[i][j] == WHITESTONE) whiteStones++;
	      }
	  black_Score = (float)blackTerritory + (float)blackStones;
	  white_Score = (float)whiteTerritory + (float)whiteStones;
	  white_Score += (handicap == 0)?KOMI:0.5;
	  [TypeOfScoring setStringValue:"Chinese Scoring Method"];
	  [BlackTerrString setStringValue:"Territory"];
	  [WhiteTerrString setStringValue:"Territory"];
	  [BlackTerrValue setIntValue:blackTerritory];
	  [WhiteTerrValue setIntValue:whiteTerritory];
	  [BlackPrisonString setStringValue:"Stones"];
	  [WhitePrisonString setStringValue:"Stones"];
	  [BlackPrisonValue setIntValue:blackStones];
	  [WhitePrisonValue setIntValue:whiteStones];
	  [BlackTotalValue setFloatValue:black_Score];
	  [WhiteTotalValue setFloatValue:white_Score];
	}
      if (black_Score > white_Score)
	sprintf(s, "Result:  Black wins by %3.1f points.", black_Score - white_Score);
      if (white_Score > black_Score)
	sprintf(s, "Result:  White wins by %3.1f points.", white_Score - black_Score);
      if (black_Score == white_Score)
	sprintf(s, "Result:  The game was a tie.");
      [KomiValue setFloatValue:((handicap == 0)?KOMI:0.5)];
      [GameResult setStringValue:s];
      [ScoringWindow makeKeyAndOrderFront:self];
      [gameMessage setStringValue:"Game Over"];
      [self lockFocus];
      [self display];
      [self unlockFocus];
    }
  
  return self;
}

- scoreGame
{
  int i, j, k, l, changes = 1, num_in_pattern;

  for (i = 0; i < MAXX; i++)
    for (j = 0; j < MAXY; j++)
      scoringmat[i][j] = 0;
      
  while (changes)
    {
      changes = 0;
      find_owner();

      for (i = 0; i < MAXX; i++)
	for (j = 0; j < MAXY; j++)
	  if ((p[i][j] != 0) && (scoringmat[i][j] == 0))
	    {
	      if (surrounds_territory(i, j))
		{
		  find_pattern_in_board(i, j);

		  for (k = 0; k < MAXX; k++)
		    for (l = 0; l < MAXY; l++)
		      if (patternmat[k][l])
			scoringmat[k][l] = p[k][l];
		}
	      else
		{
		  find_pattern_in_board(i, j);
		  set_temp_to_p();
		  num_in_pattern = 0;

		  for (k = 0; k < MAXX; k++)
		    for (l = 0; l < MAXY; l++)
		      if (patternmat[k][l])
			{
			  p[k][l] = EMPTY;
			  [self flashStone:k:l];
			  num_in_pattern++;
			}

		  find_owner();

		  if ((ownermat[i][j] != NEUTRAL_TERR) &&
		      (ownermat[i][j] != tempmat[i][j]))
		    {
		      if (tempmat[i][j] == BLACKSTONE)
			blackCaptured += num_in_pattern;
		      else
			whiteCaptured += num_in_pattern;
		      changes++;
		      [self lockFocus];
		      [self display];
		      [self unlockFocus];
		    }
		  else
		    {
		      set_p_to_temp();
		      find_owner();
		    }
		}
	    }
    }

/*  blackTerritory = 0;
  whiteTerritory = 0;

  [self lockFocus];
  for (i = 0; i < MAXX; i++)
    for (j = 0; j < MAXY; j++)
      {
	if (ownermat[i][j] == BLACKSTONE)
	  {
	    blackTerritory++;
	    p[i][j] = BLACK_TERR;
	  }
	if (ownermat[i][j] == WHITESTONE)
	  {
	    whiteTerritory++;
	    p[i][j] = WHITE_TERR;
	  }
	if (ownermat[i][j] == NEUTRAL_TERR)
	  {
	    [self flashStone:i:j];
	    p[i][j] = NEUTRAL_TERR;
	  }
      }
  [self unlockFocus];   */

  return self;
}

// The following methods draw the pieces.
  
  - drawBlackStone:imageRep 
{
  //    PSscale (1.0, 1.0);
  
  // First draw the shadow under the stone.
    
//    PSarc (RADIUS+SHADOWOFFSET/2, RADIUS-SHADOWOFFSET/2, 
//	   RADIUS-SHADOWOFFSET, 0.0, 360.0);
//  PSsetgray (NX_DKGRAY);
//  if (NXDrawingStatus == NX_DRAWING) {
//    PSsetalpha (0.666);
//  }
//  PSfill ();
  if (NXDrawingStatus == NX_DRAWING) {
    PSsetalpha (1.0);
  }
  
  // Draw the stone.
    
    PSarc (RADIUS, RADIUS, 
	   RADIUS, 0.0, 360.0);
  PSsetgray (NX_BLACK);
  PSfill ();
  
  // And the lighter & darker spots on the stone...
    
    PSarcn (RADIUS, RADIUS, 
	    RADIUS-SHADOWOFFSET-3.0, 170.0, 100.0);
  PSarc (RADIUS, RADIUS, 
	 RADIUS-SHADOWOFFSET-2.0, 100.0, 170.0);
  PSsetgray (NX_DKGRAY);
  PSfill ();
  PSarcn (RADIUS, RADIUS, 
	  RADIUS-SHADOWOFFSET-3.0, 350.0, 280.0);
  PSarc (RADIUS, RADIUS, 
	 RADIUS-SHADOWOFFSET-2.0, 280.0, 350.0);
  PSsetgray (NX_LTGRAY);
  PSfill ();
  
  return self;
}

- drawWhiteStone:imageRep 
{
  //    PSscale (1.0, 1.0);
  
  // First draw the shadow under the stone.
    
//    PSarc (RADIUS+SHADOWOFFSET/2, RADIUS-SHADOWOFFSET/2, 
//	   RADIUS-SHADOWOFFSET, 0.0, 360.0);
//  PSsetgray (NX_DKGRAY);
//  if (NXDrawingStatus == NX_DRAWING) {
//    PSsetalpha (0.666);
//  }
//  PSfill ();
  if (NXDrawingStatus == NX_DRAWING) {
    PSsetalpha (1.0);
  }
  
  // Draw the stone.
    
    PSarc (RADIUS, RADIUS, 
	   RADIUS, 0.0, 360.0);
  PSsetgray (NX_WHITE);
  PSfill ();
  
  // And the lighter & darker spots on the stone...
    
    PSarcn (RADIUS, RADIUS, 
	    RADIUS-SHADOWOFFSET-3.0, 170.0, 100.0);
  PSarc (RADIUS, RADIUS, 
	 RADIUS-SHADOWOFFSET-2.0, 100.0, 170.0);
  PSsetgray (NX_LTGRAY);
  PSfill ();
  PSarcn (RADIUS, RADIUS, 
	  RADIUS-SHADOWOFFSET-3.0, 350.0, 280.0);
  PSarc (RADIUS, RADIUS, 
	 RADIUS-SHADOWOFFSET-2.0, 280.0, 350.0);
  PSsetgray (NX_DKGRAY);
  PSfill ();
  
  return self;
}

- drawGrayStone:imageRep 
{
  //    PSscale (1.0, 1.0);
  
  // First draw the shadow under the stone.
    
//    PSarc (RADIUS+SHADOWOFFSET/2, RADIUS-SHADOWOFFSET/2, 
//	   RADIUS-SHADOWOFFSET, 0.0, 360.0);
//  PSsetgray (NX_DKGRAY);
//  if (NXDrawingStatus == NX_DRAWING) {
//    PSsetalpha (0.666);
//  }
//  PSfill ();
  if (NXDrawingStatus == NX_DRAWING) {
    PSsetalpha (1.0);
  }
  
  // Draw the stone.
    
//    PSarc (RADIUS-SHADOWOFFSET/2, RADIUS+SHADOWOFFSET/2, 
//	   RADIUS-SHADOWOFFSET, 0.0, 360.0);
    PSarc (RADIUS, RADIUS, 
	   RADIUS, 0.0, 360.0);
  PSsetgray (NX_DKGRAY);
  PSfill ();
  
  // And the lighter & darker spots on the stone...
    
    PSarcn (RADIUS, RADIUS, 
	    RADIUS-SHADOWOFFSET-3.0, 170.0, 100.0);
  PSarc (RADIUS, RADIUS, 
	 RADIUS-SHADOWOFFSET-2.0, 100.0, 170.0);
  PSsetgray (NX_LTGRAY);
  PSfill ();
  PSarcn (RADIUS, RADIUS, 
	  RADIUS-SHADOWOFFSET-3.0, 350.0, 280.0);
  PSarc (RADIUS, RADIUS, 
	 RADIUS-SHADOWOFFSET-2.0, 280.0, 350.0);
  PSsetgray (NX_WHITE);
  PSfill ();
  
  return self;
}

- drawUpperLeft:imageRep
{
  PSsetgray(NX_BLACK);
  PSsetlinewidth(0.0);
  if (NXDrawingStatus == NX_DRAWING) {
    PSsetalpha (1.0);
  }

  PSnewpath();
  PSmoveto(RADIUS, RADIUS);
  PSlineto(bounds.size.width,RADIUS);
  PSmoveto(RADIUS, RADIUS);
  PSlineto(RADIUS, 0.0);
  PSmoveto(RADIUS-SHADOWOFFSET, RADIUS+SHADOWOFFSET);
  PSlineto(bounds.size.width, RADIUS+SHADOWOFFSET);
  PSmoveto(RADIUS-SHADOWOFFSET, RADIUS+SHADOWOFFSET);
  PSlineto(RADIUS-SHADOWOFFSET, 0.0);
  PSstroke();

  return self;
}

- drawUpperRight:imageRep
{
  PSsetgray(NX_BLACK);
  PSsetlinewidth(0.0);
  if (NXDrawingStatus == NX_DRAWING) {
    PSsetalpha (1.0);
  }

  PSnewpath();
  PSmoveto(RADIUS, RADIUS);
  PSlineto(0.0,RADIUS);
  PSmoveto(RADIUS, RADIUS);
  PSlineto(RADIUS, 0.0);
  PSmoveto(RADIUS+SHADOWOFFSET, RADIUS+SHADOWOFFSET);
  PSlineto(0.0, RADIUS+SHADOWOFFSET);
  PSmoveto(RADIUS+SHADOWOFFSET, RADIUS+SHADOWOFFSET);
  PSlineto(RADIUS+SHADOWOFFSET, 0.0);
  PSstroke();

  return self;
}

- drawLowerLeft:imageRep
{
  PSsetgray(NX_BLACK);
  PSsetlinewidth(0.0);
  if (NXDrawingStatus == NX_DRAWING) {
    PSsetalpha (1.0);
  }

  PSnewpath();
  PSmoveto(RADIUS, RADIUS);
  PSlineto(bounds.size.width,RADIUS);
  PSmoveto(RADIUS, RADIUS);
  PSlineto(RADIUS, bounds.size.height);
  PSmoveto(RADIUS-SHADOWOFFSET, RADIUS-SHADOWOFFSET);
  PSlineto(bounds.size.width, RADIUS-SHADOWOFFSET);
  PSmoveto(RADIUS-SHADOWOFFSET, RADIUS-SHADOWOFFSET);
  PSlineto(RADIUS-SHADOWOFFSET, bounds.size.height);
  PSstroke();

  return self;
}

- drawLowerRight:imageRep
{
  PSsetgray(NX_BLACK);
  PSsetlinewidth(0.0);
  if (NXDrawingStatus == NX_DRAWING) {
    PSsetalpha (1.0);
  }

  PSnewpath();
  PSmoveto(RADIUS, RADIUS);
  PSlineto(0.0,RADIUS);
  PSmoveto(RADIUS, RADIUS);
  PSlineto(RADIUS, bounds.size.height);
  PSmoveto(RADIUS+SHADOWOFFSET, RADIUS-SHADOWOFFSET);
  PSlineto(0.0, RADIUS-SHADOWOFFSET);
  PSmoveto(RADIUS+SHADOWOFFSET, RADIUS-SHADOWOFFSET);
  PSlineto(RADIUS+SHADOWOFFSET, bounds.size.height);
  PSstroke();

  return self;
}

- drawMidLeft:imageRep
{
  PSsetgray(NX_BLACK);
  PSsetlinewidth(0.0);
  if (NXDrawingStatus == NX_DRAWING) {
    PSsetalpha (1.0);
  }

  PSnewpath();
  PSmoveto(RADIUS, RADIUS);
  PSlineto(bounds.size.width,RADIUS);
  PSmoveto(RADIUS, bounds.size.height);
  PSlineto(RADIUS, 0.0);
  PSmoveto(RADIUS-SHADOWOFFSET, bounds.size.height);
  PSlineto(RADIUS-SHADOWOFFSET, 0.0);
  PSstroke();

  return self;
}

- drawMidRight:imageRep
{
  PSsetgray(NX_BLACK);
  PSsetlinewidth(0.0);
  if (NXDrawingStatus == NX_DRAWING) {
    PSsetalpha (1.0);
  }

  PSnewpath();
  PSmoveto(RADIUS, RADIUS);
  PSlineto(0.0,RADIUS);
  PSmoveto(RADIUS, bounds.size.height);
  PSlineto(RADIUS, 0.0);
  PSmoveto(RADIUS+SHADOWOFFSET, bounds.size.height);
  PSlineto(RADIUS+SHADOWOFFSET, 0.0);
  PSstroke();

  return self;
}

- drawMidTop:imageRep
{
  PSsetgray(NX_BLACK);
  PSsetlinewidth(0.0);
  if (NXDrawingStatus == NX_DRAWING) {
    PSsetalpha (1.0);
  }

  PSnewpath();
  PSmoveto(RADIUS, RADIUS);
  PSlineto(RADIUS,0.0);
  PSmoveto(0.0, RADIUS);
  PSlineto(bounds.size.width, RADIUS);
  PSmoveto(0.0, RADIUS+SHADOWOFFSET);
  PSlineto(bounds.size.width, RADIUS+SHADOWOFFSET);
  PSstroke();

  return self;
}

- drawMidBottom:imageRep
{
  PSsetgray(NX_BLACK);
  PSsetlinewidth(0.0);
  if (NXDrawingStatus == NX_DRAWING) {
    PSsetalpha (1.0);
  }

  PSnewpath();
  PSmoveto(RADIUS, RADIUS);
  PSlineto(RADIUS,bounds.size.height);
  PSmoveto(0.0, RADIUS);
  PSlineto(bounds.size.width, RADIUS);
  PSmoveto(0.0, RADIUS-SHADOWOFFSET);
  PSlineto(bounds.size.width, RADIUS-SHADOWOFFSET);
  PSstroke();

  return self;
}

- drawInnerSquare:imageRep
{
  PSsetgray(NX_BLACK);
  PSsetlinewidth(0.0);
  if (NXDrawingStatus == NX_DRAWING) {
    PSsetalpha (1.0);
  }

  PSnewpath();
  PSmoveto(0.0, RADIUS);
  PSlineto(bounds.size.width,RADIUS);
  PSmoveto(RADIUS, bounds.size.height);
  PSlineto(RADIUS, 0.0);
  PSstroke();

  return self;
}

- drawInnerHandicap:imageRep
{
  PSsetgray(NX_BLACK);
  PSsetlinewidth(0.0);
  if (NXDrawingStatus == NX_DRAWING) {
    PSsetalpha (1.0);
  }

  PSnewpath();
  PSmoveto(0.0, RADIUS);
  PSlineto(bounds.size.width,RADIUS);
  PSmoveto(RADIUS, bounds.size.height);
  PSlineto(RADIUS, 0.0);
  PSstroke();
  
  PSarc(RADIUS, RADIUS, SHADOWOFFSET, 0.0, 360.0);
  PSfill();

  return self;
}

// The following methods show or erase the stones from the board.
  
  - showBlackStone 
{
  NXRect tmpRect = {{floor(stoneX), floor(stoneY)},
		      {floor(STONEWIDTH), floor(STONEHEIGHT)}};
  [blackStone composite:NX_SOVER toPoint:&tmpRect.origin];
  return self;
}

- showWhiteStone
{
  NXRect tmpRect = {{floor(stoneX), floor(stoneY)},
		      {floor(STONEWIDTH), floor(STONEHEIGHT)}};
  [whiteStone composite:NX_SOVER toPoint:&tmpRect.origin];
  return self;
}

- showGrayStone
{
  NXRect tmpRect = {{floor(stoneX), floor(stoneY)},
		      {floor(STONEWIDTH), floor(STONEHEIGHT)}};
  [grayStone composite:NX_SOVER toPoint:&tmpRect.origin];
  return self;
}

- eraseStone
{
  NXRect tmpRect = {{floor(stoneX), floor(stoneY)}, {floor(STONEWIDTH), floor(STONEHEIGHT)}};
  return [self drawBackground:&tmpRect];
}

// drawBackground: just draws the specified piece of the background by
  // compositing from the background image.

  - showBackgroundPiece: (int)x: (int)y
{
  int q;
  NXRect tmpRect = {{floor(stoneX), floor(stoneY)}, {floor(STONEWIDTH), floor(STONEHEIGHT)}};

  if ((x == 0) && (y == 0))
    [upperLeft composite:NX_SOVER toPoint:&tmpRect.origin];
  
  if ((x == 0) && (y == MAXY - 1))
    [lowerLeft composite:NX_SOVER toPoint:&tmpRect.origin];
  
  if ((x == MAXX - 1) && (y == 0))
    [upperRight composite:NX_SOVER toPoint:&tmpRect.origin];
  
  if ((x == MAXX - 1) && (y == MAXY - 1))
    [lowerRight composite:NX_SOVER toPoint:&tmpRect.origin];
  
  if ((x == 0) && (y > 0) && (y < MAXY - 1))
    [midLeft composite:NX_SOVER toPoint:&tmpRect.origin];
  
  if ((x == MAXX - 1) && (y > 0) && (y < MAXY - 1))
    [midRight composite:NX_SOVER toPoint:&tmpRect.origin];
  
  if ((x > 0) && (x < MAXX - 1) && (y == 0))
    [midTop composite:NX_SOVER toPoint:&tmpRect.origin];
  
  if ((x > 0) && (x < MAXX - 1) && (y == MAXY - 1))
    [midBottom composite:NX_SOVER toPoint:&tmpRect.origin];
  
  if ((x > 0) && (x < MAXX - 1) && (y > 0) && (y < MAXY - 1))
    [innerSquare composite:NX_SOVER toPoint:&tmpRect.origin];
    
  if (MAXX < 13)
    q = 2;
  else
    q = 3;
  
  if (((x == q) && (y == q)) || ((x == q) && (y == MAXY/2)) ||
      ((x == q) && (y == MAXY-q-1)) || ((x == MAXX/2) && (y == q)) ||
      ((x == MAXX/2) && (y == MAXY/2)) || ((x == MAXX/2) && (y == MAXY-q-1)) ||
      ((x == MAXX-q-1) && (y == q)) || ((x == MAXX-q-1) && (y == MAXY/2)) ||
      ((x == MAXX-q-1) && (y == MAXY-q-1)))
    [innerHandicap composite:NX_SOVER toPoint:&tmpRect.origin];

  return self;
}
  
  - drawBackground:(NXRect *)rect
{
  NXRect tmpRect = *rect;
  
  NX_X(&tmpRect) = floor(NX_X(&tmpRect));
  NX_Y(&tmpRect) = floor(NX_Y(&tmpRect));
  if (NXDrawingStatus == NX_DRAWING) {
    PSsetgray (NX_WHITE);
    PScompositerect (NX_X(&tmpRect), NX_Y(&tmpRect),
		     NX_WIDTH(&tmpRect), NX_HEIGHT(&tmpRect), NX_COPY);
  }
  [backGround composite:NX_SOVER fromRect:&tmpRect toPoint:&tmpRect.origin];
  return self;
}

// drawSelf::, a method every decent View should have, redraws the game
  // in its current state. This allows us to print the game very easily.
  
  - drawSelf:(NXRect *)rects :(int)rectCount 
{
  int xcnt, ycnt;
  char s[5], specialChar;
  
  [self drawBackground:(rects ? rects : &bounds)];

  specialChar = 'a';

  for (xcnt = 0; xcnt < MAXX; xcnt++)
    {
      for (ycnt = 0; ycnt < MAXY; ycnt++)
	{
	  setStoneLoc(xcnt, ycnt);

	  switch (p[xcnt][ycnt])
	    {
	    case EMPTY: currentCharacter = 0;
	      [self showBackgroundPiece: xcnt: ycnt];
	      break;
	    case WHITESTONE: [self showWhiteStone];
	      if ([showHistFlag intValue] && !SmartGoGameFlag)
		{
		  char s[5];
		  
		  [historyFont set];
		  PSsetgray(NX_BLACK);
		  PSmoveto(stoneX+RADIUS -
			   (floor(log(hist[xcnt][ycnt]+0.5)/log(10))+1.0)*3,
			   stoneY+RADIUS - 4);
		  if (hist[xcnt][ycnt] > 0)
		    {
		      sprintf(s, "%d", hist[xcnt][ycnt]);
		      PSshow(s);
		    }
		  else
		    {
		      PSmoveto(stoneX + RADIUS - 4, stoneY + RADIUS - 4);
		      PSshow("H");
		    }
		}
	      break;
	    case BLACKSTONE: [self showBlackStone];
	      if ([showHistFlag intValue] && !SmartGoGameFlag)
		{
		  char s[5];

		  [historyFont set];
		  PSsetgray(NX_WHITE);
		  PSmoveto(stoneX+RADIUS -
			   (floor(log(hist[xcnt][ycnt]+0.5)/log(10))+1.0)*3,
			   stoneY+RADIUS - 4);
		  if (hist[xcnt][ycnt] > 0)
		    {
		      sprintf(s, "%d", hist[xcnt][ycnt]);
		      PSshow(s);
		    }
		  else
		    {
		      PSmoveto(stoneX + RADIUS - 4, stoneY + RADIUS - 4);
		      PSshow("H");
		    }
		}
	      break;
	    case NEUTRAL_TERR: [self showGrayStone];
	      break;
	    case WHITE_TERR: [self showBackgroundPiece: xcnt: ycnt];
	      [whiteTerrFont set];
	      PSsetgray(NX_WHITE);
	      PSmoveto(stoneX+RADIUS/3, stoneY+RADIUS/3+2);
	      PSshow("W");
	      break;
	    case BLACK_TERR: [self showBackgroundPiece: xcnt: ycnt];
	      [blackTerrFont set];
	      PSsetgray(NX_DKGRAY);
	      PSmoveto(stoneX+RADIUS/3+1, stoneY+RADIUS/3);
	      PSshow("B");
	      break;
	    case SPECIAL_CHAR: [self showBackgroundPiece: xcnt: ycnt];
	      PSselectfont("Helvetica", 25.0);
	      PSsetgray(NX_DKGRAY);
	      PSmoveto(stoneX+RADIUS/3+1, stoneY+RADIUS/3);
	      sprintf(s,"%c",specialChar);
	      specialChar++;
	      PSshow(s);
	      break;
	    default: currentCharacter = 0;
	      [self showBackgroundPiece: xcnt: ycnt];
	      break;
	    }
	}
    }

  if ([showCoords intValue])
    {
      for (xcnt = 0; xcnt < MAXX; xcnt++)
	{
	  setStoneLoc(xcnt, 0);

	  [historyFont set];
	  PSsetgray(NX_DKGRAY);
	  PSmoveto(stoneX + RADIUS - 3, stoneY + RADIUS + 11);
	  s[0] = 'A' + xcnt;
	  if (xcnt > 7) s[0]++;
	  s[1] = 0;
	  PSshow(s);

          setStoneLoc(xcnt, MAXY - 1);
          PSmoveto(stoneX + RADIUS - 3, stoneY - 3);
	  PSshow(s);
	}
      for (ycnt = 0; ycnt < MAXX; ycnt++)
	{
	  setStoneLoc(0, ycnt);

	  [historyFont set];
	  PSsetgray(NX_DKGRAY);
	  PSmoveto(stoneX - 4, stoneY + RADIUS - 4);
	  sprintf(s, "%d", MAXY-ycnt);
	  PSshow(s);

	  setStoneLoc(MAXX - 1, ycnt);
	  if (xcnt < 10)
	    {
	      PSmoveto(stoneX + STONEWIDTH, stoneY + RADIUS - 4);
	    }
	  else
	    {
	      PSmoveto(stoneX + STONEWIDTH - 6, stoneY + RADIUS - 4);
	    }
	  PSshow(s);
	}
    }
  
  return self;
}

- print:sender
{
  return [self printPSCode:sender];
}

- step
{
  NXEvent peek_ev, *get_ev;
  
  if (gameType == IGSGAME)
    {
      return self;
    }
  
  if (neitherSide)
    return self;
    
  if (((currentStone == BLACKSTONE) && (blackSide == 0)) ||
      ((currentStone == WHITESTONE) && (whiteSide == 0)))
    return self;
  
  if (bothSides) {
    while ((gameRunning) && (!finished)) {
      [self selectMove];
      NXPing();
      [self updateInfo];

      if( [NXApp peekNextEvent: NX_MOUSEDOWNMASK into: &peek_ev] ){
	get_ev = [NXApp getNextEvent: NX_MOUSEDOWNMASK];
	[NXApp sendEvent: get_ev];
      }
    }
  
    NXPing();
  } else {
    [passButton setEnabled: NO];
    [passButton display];
    [self selectMove];

    NXPing();
    [self updateInfo];

    NXPing();
    [passButton setEnabled: YES];
    [passButton display];

    NXPing();
  }
  return self;
}

- selectMove
{
  int i, j;
  
  NXPing();

  if( !bothSides )
    [stopButton setEnabled: NO];
  else
    [stopButton setEnabled: YES];
  
  genmove( &i, &j );
  if (i >= 0)
    {
      p[i][j] = currentStone;

      [self flashStone: i: j];
    }

  [self addMoveToGameMoves: currentStone: i: j];
  
  if (((i < 0) || (j < 0)) && (AGAScoring))
    {
      if (currentStone == BLACKSTONE)
        blackCaptured++;
      else
        whiteCaptured++;
    }
  
  [self selectMoveEnd];
    
  if (i >= 0)
    {
      [self lockFocus];
      if (currentStone == BLACKSTONE)
        [self showBlackStone];
      else
        [self showWhiteStone];
      [self unlockFocus];

      if ([playSounds intValue])
	[stoneClick play];
      NXPing();
    }
    
  NXPing();
  return self;
}

- selectMoveEnd
{
  NXPing();

  [startButton setEnabled: YES];
  [stopButton setEnabled: YES];
  NXPing();

  return self;
}

- flashStone: (int)x :(int)y
{
  
  setStoneLoc(x, y);
  
  [self lockFocus];
  [self showGrayStone];
  [self unlockFocus];
  
  return self;
}

- setMess1:(char *)s
{
  [gameMessage setStringValue:s];
  [gameMessage display];

  return self;
}

- setMess2:(char *)s
{
  [gameMessage2 setStringValue:s];
  [gameMessage2 display];

  return self;
}

- setblacksPrisoners:(int)bp
{
  [blacksPrisoners setIntValue:bp];
  [blacksPrisoners display];

  return self;
}

- setwhitesPrisoners:(int)wp
{
  [whitesPrisoners setIntValue:wp];
  [whitesPrisoners display];

  return self;
}


@end

